﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Speech.Recognition;
using System.Threading;
using System.ComponentModel;

using KnowledgeBase;
using KnowledgeBaseIntermediary;
using LisaConfigurator;

namespace InputHandler
{
    /// <summary>
    /// This class handles all the User Commands. It handles the speech recognition and informs the core 
    /// of any recognized user commands. When a state change occurs, it adapts its speech recognition grammar to only
    /// contain the necessary commands of the new state as well as the global state change commands and any supplementary
    /// commands.
    /// </summary>
    class UserCommandHandler
    {
        public event HandleInput userCommandReceived;    //Gets fired when a user command is received

        //Speech recognition-related declarations
        SpeechRecognizer speechReco = new SpeechRecognizer();

        //Other declarations
        IKnowledgeBase knowledgeBase;
        IConfigurationAccessor lisaConfigurator;

        public UserCommandHandler(IKnowledgeBase knowledgeBase, IConfigurationAccessor lisaConfigurator)
        {
            this.knowledgeBase = knowledgeBase;
            this.lisaConfigurator = lisaConfigurator;
            //Init speech recog
            loadStandbyStateGrammar();
            loadSupplementaryGrammar();
            speechReco.Enabled = true;
            //Listen to Speech Recognizer's events
            speechReco.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(speechReco_SpeechRecognized);
            speechReco.SpeechDetected += new EventHandler<SpeechDetectedEventArgs>(speechReco_SpeechDetected);
        }

        #region public methods
        /// <summary>
        /// Changes the grammar to reflect the keywords of the new state (commands and scenes).
        /// </summary>
        /// <param name="newState"></param>
        public void changeState(StateDTO newState)
        {
            if (newState == null)
                loadStandbyStateGrammar();
            else
                loadActiveStateGrammar(newState);
            loadSupplementaryGrammar();
        }
        #endregion

        #region private helper methods
        void loadStandbyStateGrammar()
        {
            speechReco.UnloadAllGrammars();
            //Build grammar list
            List<string> allStates = knowledgeBase.getAllAvailableStates();
            List<String> grammarList = new List<String>();
            //Prepend each statename with State-change Keyword
            foreach (string stateName in allStates)
            {
                grammarList.Add(lisaConfigurator.getStateChangePrefix() + stateName);
            }
            //Add grammar to Speech Recognizer
            Choices myChoices = new Choices(grammarList.ToArray());
            GrammarBuilder builder = new GrammarBuilder(myChoices);
            // builder.Culture = new System.Globalization.CultureInfo("en-US");
            //builder.Culture = new System.Globalization.CultureInfo("de-DE");
            Grammar gram = new Grammar(builder);
            speechReco.LoadGrammar(gram);
        }

        void loadActiveStateGrammar(StateDTO newState)
        {
            speechReco.UnloadAllGrammars();
            //Build grammar list
            List<String> grammarList = new List<String>();
            List<string> allStates = knowledgeBase.getAllAvailableStates();
            List<string> manualCommands = newState.getManualCommands();
            List<string> sceneCommands = newState.getScenes();
            //Prepend each statename with State-change Keyword
            foreach (string stateName in allStates)
            {
                grammarList.Add(lisaConfigurator.getStateChangePrefix() + stateName);
            }
            foreach (string manualCommand in manualCommands)
            {
                grammarList.Add(manualCommand);
            }
            //Prepend scene with the scene-prefix
            foreach (string scene in sceneCommands)
            {
                grammarList.Add(lisaConfigurator.getScenePrefix() + scene);
            }
            //Add grammar to Speech Recognizer
            Choices myChoices = new Choices(grammarList.ToArray());
            GrammarBuilder builder = new GrammarBuilder(myChoices);
            // builder.Culture = new System.Globalization.CultureInfo("en-US");
            //builder.Culture = new System.Globalization.CultureInfo("de-DE");
            Grammar gram = new Grammar(builder);
            speechReco.LoadGrammar(gram);
        }

        /// <summary>
        /// Loads additional vocabulary needed for exiting active state and giving/denying permission etc.
        /// </summary>
        void loadSupplementaryGrammar()
        {
            List<String> grammarList = new List<String>();
            grammarList.AddRange(lisaConfigurator.getReturnToStandbyCommands());
            grammarList.AddRange(lisaConfigurator.getGrantPermissionCommands());
            grammarList.AddRange(lisaConfigurator.getDenyPermissionCommands());
            //Add grammar to Speech Recognizer 
            Choices myChoices = new Choices(grammarList.ToArray());
            GrammarBuilder builder = new GrammarBuilder(myChoices);
            // builder.Culture = new System.Globalization.CultureInfo("en-US");
            //builder.Culture = new System.Globalization.CultureInfo("de-DE");
            Grammar gram = new Grammar(builder);
            speechReco.LoadGrammar(gram);
        }
        #endregion

        #region Listener methods for speech recognizer
        void speechReco_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            FireUserCommandReceived(new InputEventArgs(e.Result.Text));
        }

        void speechReco_SpeechDetected(object sender, SpeechDetectedEventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("Detected speech");
        }
        #endregion

        #region Event-related methods
        void FireUserCommandReceived(InputEventArgs eventArgs)
        {
            // Get local event for thread safety purposes
            HandleInput handler = userCommandReceived;
            FireHandleInputEvent(handler, eventArgs);
        }

        protected void FireHandleInputEvent(HandleInput handler, InputEventArgs eventArgs)
        {
            if (handler != null)
            {
                foreach (HandleInput singleCast in handler.GetInvocationList())
                {
                    ISynchronizeInvoke syncInvoke = singleCast.Target as ISynchronizeInvoke;
                    try
                    {
                        if (syncInvoke != null && syncInvoke.InvokeRequired)
                        {
                            // Invokie the event on the main thread
                            syncInvoke.Invoke(handler, new object[] { eventArgs });
                        }
                        else
                        {
                            // Raise the event
                            singleCast(eventArgs);
                        }
                    }
                    catch
                    {
                    }
                }
            }
        }
        #endregion
    }
}
